home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / UpdateManager / UpdateManager.py < prev   
Encoding:
Python Source  |  2009-04-27  |  34.2 KB  |  918 lines

  1. # UpdateManager.py 
  2. #  
  3. #  Copyright (c) 2004-2008 Canonical
  4. #                2004 Michiel Sikkes
  5. #                2005 Martin Willemoes Hansen
  6. #  
  7. #  Author: Michiel Sikkes <michiel@eyesopened.nl>
  8. #          Michael Vogt <mvo@debian.org>
  9. #          Martin Willemoes Hansen <mwh@sysrq.dk>
  10. #  This program is free software; you can redistribute it and/or 
  11. #  modify it under the terms of the GNU General Public License as 
  12. #  published by the Free Software Foundation; either version 2 of the
  13. #  License, or (at your option) any later version.
  14. #  This program is distributed in the hope that it will be useful,
  15. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. #  GNU General Public License for more details.
  18. #  You should have received a copy of the GNU General Public License
  19. #  along with this program; if not, write to the Free Software
  20. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21. #  USA
  22.  
  23. import pygtk
  24. pygtk.require('2.0')
  25. import gtk
  26. import gtk.gdk
  27. import gtk.glade
  28. try:
  29.     import gconf
  30. except:
  31.     import fakegconf as gconf
  32. import gobject
  33.  
  34. import warnings
  35. warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
  36. import apt
  37. import apt_pkg
  38.  
  39. import gettext
  40. import copy
  41. import string
  42. import sys
  43. import os
  44. import os.path
  45. import stat
  46. import re
  47. import locale
  48. import tempfile
  49. import pango
  50. import subprocess
  51. import pwd
  52. import urllib2
  53. import httplib
  54. import socket
  55. import time
  56. import thread
  57. import xml.sax.saxutils
  58.  
  59. import dbus
  60. import dbus.service
  61. import dbus.glib
  62.  
  63. import GtkProgress
  64.  
  65. from gettext import gettext as _
  66. from gettext import ngettext
  67.  
  68.  
  69. from Core.utils import *
  70. from Core.UpdateList import UpdateList
  71. from Core.MyCache import MyCache, NotEnoughFreeSpaceError
  72. from Core.MetaRelease import Dist
  73.  
  74. from DistUpgradeFetcher import DistUpgradeFetcherGtk
  75. from ChangelogViewer import ChangelogViewer
  76. from SimpleGladeApp import SimpleGladeApp
  77. from HelpViewer import HelpViewer
  78. from MetaReleaseGObject import MetaRelease
  79.  
  80. #import pdb
  81.  
  82. # FIXME:
  83. # - kill "all_changes" and move the changes into the "Update" class
  84.  
  85. # list constants
  86. (LIST_CONTENTS, LIST_NAME, LIST_PKG, LIST_ORIGIN) = range(4)
  87.  
  88. # actions for "invoke_manager"
  89. (INSTALL, UPDATE) = range(2)
  90.  
  91.  
  92. class UpdateManagerDbusControler(dbus.service.Object):
  93.     """ this is a helper to provide the UpdateManagerIFace """
  94.     def __init__(self, parent, bus_name,
  95.                  object_path='/org/freedesktop/UpdateManagerObject'):
  96.         dbus.service.Object.__init__(self, bus_name, object_path)
  97.         self.parent = parent
  98.  
  99.     @dbus.service.method('org.freedesktop.UpdateManagerIFace')
  100.     def bringToFront(self):
  101.         self.parent.window_main.present()
  102.         return True
  103.  
  104. class UpdateManager(SimpleGladeApp):
  105.  
  106.   def __init__(self, datadir, options):
  107.     self.setupDbus()
  108.     gtk.window_set_default_icon_name("update-manager")
  109.  
  110.     self.datadir = datadir
  111.     SimpleGladeApp.__init__(self, datadir+"glade/UpdateManager.glade",
  112.                             None, domain="update-manager")
  113.  
  114.     self.image_logo.set_from_icon_name("update-manager", gtk.ICON_SIZE_DIALOG)
  115.     self.window_main.set_sensitive(False)
  116.     self.window_main.grab_focus()
  117.     self.button_close.grab_focus()
  118.     self.dl_size = 0
  119.  
  120.     # create text view
  121.     self.textview_changes = ChangelogViewer()
  122.     self.textview_changes.show()
  123.     self.scrolledwindow_changes.add(self.textview_changes)
  124.     changes_buffer = self.textview_changes.get_buffer()
  125.     changes_buffer.create_tag("versiontag", weight=pango.WEIGHT_BOLD)
  126.  
  127.     # expander
  128.     self.expander_details.connect("notify::expanded", self.activate_details)
  129.  
  130.     # useful exit stuff
  131.     self.window_main.connect("delete_event", self.close)
  132.     self.button_close.connect("clicked", lambda w: self.exit())
  133.  
  134.     # the treeview (move into it's own code!)
  135.     self.store = gtk.ListStore(str, str, gobject.TYPE_PYOBJECT, 
  136.                                gobject.TYPE_PYOBJECT)
  137.     self.treeview_update.set_model(self.store)
  138.     self.treeview_update.set_headers_clickable(True);
  139.  
  140.     tr = gtk.CellRendererText()
  141.     tr.set_property("xpad", 6)
  142.     tr.set_property("ypad", 6)
  143.     cr = gtk.CellRendererToggle()
  144.     cr.set_property("activatable", True)
  145.     cr.set_property("xpad", 6)
  146.     cr.connect("toggled", self.toggled)
  147.  
  148.     column_install = gtk.TreeViewColumn("Install", cr)
  149.     column_install.set_cell_data_func (cr, self.install_column_view_func)
  150.     column = gtk.TreeViewColumn("Name", tr, markup=LIST_CONTENTS)
  151.     column.set_resizable(True)
  152.     major,minor,patch = gtk.pygtk_version
  153.     if (major >= 2) and (minor >= 5):
  154.       column_install.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
  155.       column_install.set_fixed_width(30)
  156.       column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
  157.       column.set_fixed_width(100)
  158.       self.treeview_update.set_fixed_height_mode(False)
  159.  
  160.     self.treeview_update.append_column(column_install)
  161.     column_install.set_visible(True)
  162.     self.treeview_update.append_column(column)
  163.     self.treeview_update.set_search_column(LIST_NAME)
  164.     self.treeview_update.connect("button-press-event", self.show_context_menu)
  165.     self.treeview_update.connect("row-activated", self.row_activated)
  166.  
  167.     # setup the help viewer and disable the help button if there
  168.     # is no viewer available
  169.     #self.help_viewer = HelpViewer("update-manager")
  170.     #if self.help_viewer.check() == False:
  171.     #    self.button_help.set_sensitive(False)
  172.  
  173.     if not os.path.exists("/usr/bin/software-properties-gtk"):
  174.         self.button_settings.set_sensitive(False)
  175.  
  176.     self.gconfclient = gconf.client_get_default()
  177.     init_proxy(self.gconfclient)
  178.     # init show version
  179.     try:
  180.         self.show_versions = self.gconfclient.get_bool("/apps/update-manager/show_versions")
  181.     except gobject.GError, e:
  182.         self.show_versions = False
  183.     # keep track when we run (for update-notifier)
  184.     try:
  185.         self.gconfclient.set_int("/apps/update-manager/launch_time", int(time.time()))
  186.     except gobject.GError, e:
  187.         print "Error setting launch_time: ", e
  188.     # get progress object
  189.     self.progress = GtkProgress.GtkOpProgress(self.dialog_cacheprogress,
  190.                                               self.progressbar_cache,
  191.                                               self.label_cache,
  192.                                               self.window_main)
  193.     # restore state
  194.     self.restore_state()
  195.     # deal with no-focus-on-map
  196.     if options.no_focus_on_map:
  197.         self.window_main.set_focus_on_map(False)
  198.         self.progress._window.set_focus_on_map(False)
  199.     # show the main window
  200.     self.window_main.show()
  201.     # it can only the iconified *after* it is shown (even if the docs
  202.     # claim otherwise)
  203.     if options.no_focus_on_map:
  204.         self.window_main.iconify()
  205.         self.window_main.set_urgency_hint(True)
  206.         self.window_main.connect("focus-in-event",
  207.                                  lambda w,e: (w.set_urgency_hint(False) and False))
  208.  
  209.  
  210.   def install_column_view_func(self, cell_layout, renderer, model, iter):
  211.     pkg = model.get_value(iter, LIST_PKG)
  212.     # hide it if we are only a header line
  213.     renderer.set_property("visible", pkg != None)
  214.     if pkg is None:
  215.         return
  216.     to_install = pkg.markedInstall or pkg.markedUpgrade
  217.     renderer.set_property("active", to_install)
  218.     if pkg.name in self.list.held_back:
  219.         renderer.set_property("activatable", False)
  220.     else: 
  221.         renderer.set_property("activatable", True)
  222.  
  223.   def setupDbus(self):
  224.     """ this sets up a dbus listener if none is installed alread """
  225.     # check if there is another g-a-i already and if not setup one
  226.     # listening on dbus
  227.     try:
  228.         bus = dbus.SessionBus()
  229.     except:
  230.         print "warning: could not initiate dbus"
  231.         return
  232.     try:
  233.         proxy_obj = bus.get_object('org.freedesktop.UpdateManager', 
  234.                                    '/org/freedesktop/UpdateManagerObject')
  235.         iface = dbus.Interface(proxy_obj, 'org.freedesktop.UpdateManagerIFace')
  236.         iface.bringToFront()
  237.         #print "send bringToFront"
  238.         sys.exit(0)
  239.     except dbus.DBusException, e:
  240.          #print "no listening object (%s) "% e
  241.          bus_name = dbus.service.BusName('org.freedesktop.UpdateManager',bus)
  242.          self.dbusControler = UpdateManagerDbusControler(self, bus_name)
  243.  
  244.  
  245.   def on_checkbutton_reminder_toggled(self, checkbutton):
  246.     self.gconfclient.set_bool("/apps/update-manager/remind_reload",
  247.                               not checkbutton.get_active())
  248.  
  249.   def close(self, widget, data=None):
  250.     if self.window_main.get_property("sensitive") is False:
  251.         return True
  252.     else:
  253.         self.exit()
  254.  
  255.   
  256.   def set_changes_buffer(self, changes_buffer, text, name, srcpkg):
  257.     changes_buffer.set_text("")
  258.     lines = text.split("\n")
  259.     if len(lines) == 1:
  260.       changes_buffer.set_text(text)
  261.       return
  262.     
  263.     for line in lines:
  264.       end_iter = changes_buffer.get_end_iter()
  265.       version_match = re.match(r'^%s \((.*)\)(.*)\;.*$' % re.escape(srcpkg), line)
  266.       #bullet_match = re.match("^.*[\*-]", line)
  267.       author_match = re.match("^.*--.*<.*@.*>.*$", line)
  268.       if version_match:
  269.         version = version_match.group(1)
  270.     upload_archive = version_match.group(2).strip()
  271.         version_text = _("Version %s: \n") % version
  272.         changes_buffer.insert_with_tags_by_name(end_iter, version_text, "versiontag")
  273.       elif (author_match):
  274.         pass
  275.       else:
  276.         changes_buffer.insert(end_iter, line+"\n")
  277.         
  278.  
  279.   def on_treeview_update_cursor_changed(self, widget):
  280.     tuple = widget.get_cursor()
  281.     path = tuple[0]
  282.     # check if we have a path at all
  283.     if path == None:
  284.       return
  285.     model = widget.get_model()
  286.     iter = model.get_iter(path)
  287.  
  288.     # set descr
  289.     pkg = model.get_value(iter, LIST_PKG)
  290.     if pkg == None or pkg.description == None:
  291.       changes_buffer = self.textview_changes.get_buffer()
  292.       changes_buffer.set_text("")
  293.       desc_buffer = self.textview_descr.get_buffer()
  294.       desc_buffer.set_text("")
  295.       self.notebook_details.set_sensitive(False)
  296.       return
  297.     long_desc = pkg.description
  298.     self.notebook_details.set_sensitive(True)
  299.     # Skip the first line - it's a duplicate of the summary
  300.     i = long_desc.find("\n")
  301.     long_desc = long_desc[i+1:]
  302.     # do some regular expression magic on the description
  303.     # Add a newline before each bullet
  304.     p = re.compile(r'^(\s|\t)*(\*|0|-)',re.MULTILINE)
  305.     long_desc = p.sub('\n*', long_desc)
  306.     # replace all newlines by spaces
  307.     p = re.compile(r'\n', re.MULTILINE)
  308.     long_desc = p.sub(" ", long_desc)
  309.     # replace all multiple spaces by newlines
  310.     p = re.compile(r'\s\s+', re.MULTILINE)
  311.     long_desc = p.sub("\n", long_desc)
  312.  
  313.     desc_buffer = self.textview_descr.get_buffer()
  314.     desc_buffer.set_text(long_desc)
  315.  
  316.     # now do the changelog
  317.     name = model.get_value(iter, LIST_NAME)
  318.     if name == None:
  319.       return
  320.  
  321.     changes_buffer = self.textview_changes.get_buffer()
  322.     
  323.     # check if we have the changes already
  324.     if self.cache.all_changes.has_key(name):
  325.       changes = self.cache.all_changes[name]
  326.       self.set_changes_buffer(changes_buffer, changes[0], name, changes[1])
  327.     else:
  328.       if self.expander_details.get_expanded():
  329.         lock = thread.allocate_lock()
  330.         lock.acquire()
  331.         t=thread.start_new_thread(self.cache.get_news_and_changelog,(name,lock))
  332.         changes_buffer.set_text("%s\n" % _("Downloading list of changes..."))
  333.         iter = changes_buffer.get_iter_at_line(1)
  334.         anchor = changes_buffer.create_child_anchor(iter)
  335.         button = gtk.Button(stock="gtk-cancel")
  336.         self.textview_changes.add_child_at_anchor(button, anchor)
  337.         button.show()
  338.         id = button.connect("clicked",
  339.                             lambda w,lock: lock.release(), lock)
  340.         # wait for the dl-thread
  341.         while lock.locked():
  342.           time.sleep(0.01)
  343.           while gtk.events_pending():
  344.             gtk.main_iteration()
  345.         # download finished (or canceld, or time-out)
  346.         button.hide()
  347.         button.disconnect(id);
  348.     # display NEWS.Debian first, then the changelog
  349.     changes = ""
  350.     srcpkg = self.cache[name].sourcePackageName
  351.     if self.cache.all_news.has_key(name):
  352.         changes += self.cache.all_news[name]
  353.     if self.cache.all_changes.has_key(name):
  354.         changes += self.cache.all_changes[name]
  355.     if changes:
  356.         self.set_changes_buffer(changes_buffer, changes, name, srcpkg)
  357.  
  358.   def show_context_menu(self, widget, event):
  359.     """
  360.     Show a context menu if a right click was performed on an update entry
  361.     """
  362.     if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:
  363.         menu = gtk.Menu()
  364.         item_select_none = gtk.MenuItem(_("_Uncheck All"))
  365.         item_select_none.connect("activate", self.select_none_updgrades)
  366.         menu.add(item_select_none)
  367.         num_updates = self.cache.installCount
  368.         if num_updates == 0:
  369.             item_select_none.set_property("sensitive", False)
  370.         item_select_all = gtk.MenuItem(_("_Check All"))
  371.         item_select_all.connect("activate", self.select_all_updgrades)
  372.         menu.add(item_select_all)
  373.         menu.popup(None, None, None, 0, event.time)
  374.         menu.show_all()
  375.         return True
  376.  
  377.   def select_all_updgrades(self, widget):
  378.     """
  379.     Select all updates
  380.     """
  381.     self.setBusy(True)
  382.     self.cache.saveDistUpgrade()
  383.     self.treeview_update.queue_draw()
  384.     self.refresh_updates_count()
  385.     self.setBusy(False)
  386.  
  387.   def select_none_updgrades(self, widget):
  388.     """
  389.     Select none updates
  390.     """
  391.     self.setBusy(True)
  392.     self.cache.clear()
  393.     self.treeview_update.queue_draw()
  394.     self.refresh_updates_count()
  395.     self.setBusy(False)
  396.  
  397.   def setBusy(self, flag):
  398.       """ Show a watch cursor if the app is busy for more than 0.3 sec.
  399.       Furthermore provide a loop to handle user interface events """
  400.       if self.window_main.window is None:
  401.           return
  402.       if flag == True:
  403.           self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
  404.       else:
  405.           self.window_main.window.set_cursor(None)
  406.       while gtk.events_pending():
  407.           gtk.main_iteration()
  408.  
  409.   def refresh_updates_count(self):
  410.       self.button_install.set_sensitive(self.cache.installCount)
  411.       try:
  412.           self.dl_size = self.cache.requiredDownload
  413.           # TRANSLATORS: b stands for Bytes
  414.           self.label_downsize.set_markup(_("Download size: %s") % \
  415.                                          humanize_size(self.dl_size))
  416.       except SystemError, e:
  417.           print "requiredDownload could not be calculated: %s" % e
  418.           self.label_downsize.set_markup(_("Unknown download size"))
  419.  
  420.   def _get_last_apt_get_update_text(self):
  421.       """
  422.       return a human readable string with the information when
  423.       the last apt-get update was run
  424.       """
  425.       if not os.path.exists("/var/lib/apt/periodic/update-success-stamp"):
  426.           return _("It is unknown when the package information was "
  427.                    "updated last. Please try clicking on the 'Check' "
  428.                    "button to update the information.")
  429.       # calculate when the last apt-get update (or similar operation)
  430.       # was performed
  431.       mtime = os.stat("/var/lib/apt/periodic/update-success-stamp")[stat.ST_MTIME]
  432.       ago_days = int( (time.time() - mtime) / (24*60*60))
  433.       ago_hours = int((time.time() - mtime) / (60*60) )
  434.       if ago_days > 0:
  435.           return ngettext("The package information was last updated %s day ago.",
  436.                           "The package information was last updated %s days ago.",
  437.                           ago_days) % ago_days
  438.       elif ago_hours > 0:
  439.           return ngettext("The package information was last updated %s hour ago.",
  440.                           "The package information was last updated %s hours ago.",
  441.                           ago_hours) % ago_hours
  442.       else:
  443.           return _("The package information was last updated less than one hour ago.")
  444.       return None
  445.  
  446.   def update_count(self):
  447.       """activate or disable widgets and show dialog texts correspoding to
  448.          the number of available updates"""
  449.       self.refresh_updates_count()
  450.       num_updates = self.cache.installCount
  451.       text_label_main = _("Software updates correct errors, eliminate security vulnerabilities and provide new features.")
  452.       if num_updates == 0:
  453.           text_header= "<big><b>%s</b></big>"  % _("Your system is up-to-date")
  454.           text_download = ""
  455.           self.notebook_details.set_sensitive(False)
  456.           self.treeview_update.set_sensitive(False)
  457.           self.button_install.set_sensitive(False)
  458.           self.label_downsize.set_text=""
  459.           self.button_close.grab_default()
  460.           self.textview_changes.get_buffer().set_text("")
  461.           self.textview_descr.get_buffer().set_text("")
  462.           if self._get_last_apt_get_update_text() is not None:
  463.               text_label_main = self._get_last_apt_get_update_text()
  464.       else:
  465.           # show different text on first run (UX team suggestion)
  466.           firstrun = self.gconfclient.get_bool("/apps/update-manager/first_run")
  467.           if firstrun:
  468.               text_header = "<big><b>%s</b></big>" % _("Welcome to Ubuntu")
  469.               text_label_main = _("These software updates have been issued since Ubuntu was released. If you don't want to install them now, choose \"Update Manager\" from the Administration Menu later.")
  470.               self.gconfclient.set_bool("/apps/update-manager/first_run", False)
  471.           else:
  472.               text_header = "<big><b>%s</b></big>" % _("Software updates are available for this computer")
  473.               text_label_main = _("If you don't want to install them now, choose \"Update Manager\" from the Administration menu later.")
  474.           text_download = _("Download size: %s") % humanize_size(self.dl_size)
  475.           self.notebook_details.set_sensitive(True)
  476.           self.treeview_update.set_sensitive(True)
  477.           self.button_install.grab_default()
  478.           self.treeview_update.set_cursor(1)
  479.       self.label_header.set_markup(text_header)
  480.       self.label_downsize.set_markup(text_download)
  481.       self.label_main_details.set_text(text_label_main)
  482.  
  483.   def activate_details(self, expander, data):
  484.     expanded = self.expander_details.get_expanded()
  485.     self.vbox_updates.set_child_packing(self.expander_details,
  486.                                         expanded,
  487.                                         True,
  488.                                         0,
  489.                                         True)
  490.     self.gconfclient.set_bool("/apps/update-manager/show_details",expanded)
  491.     if expanded:
  492.       self.on_treeview_update_cursor_changed(self.treeview_update)
  493.  
  494.   def run_synaptic(self, id, action, lock):
  495.     try:
  496.       apt_pkg.PkgSystemUnLock()
  497.     except SystemError:
  498.       pass
  499.     cmd = ["/usr/bin/gksu", 
  500.            "--desktop", "/usr/share/applications/update-manager.desktop", 
  501.            "--", "/usr/sbin/synaptic", "--hide-main-window",  
  502.            "--non-interactive", "--parent-window-id", "%s" % (id) ]
  503.     if action == INSTALL:
  504.       # close when update was successful (its ok to use a Synaptic::
  505.       # option here, it will not get auto-saved, because synaptic does
  506.       # not save options in non-interactive mode)
  507.       if self.gconfclient.get_bool("/apps/update-manager/autoclose_install_window"):
  508.           cmd.append("-o")
  509.           cmd.append("Synaptic::closeZvt=true")
  510.       # custom progress strings
  511.       cmd.append("--progress-str")
  512.       cmd.append("%s" % _("Please wait, this can take some time."))
  513.       cmd.append("--finish-str")
  514.       cmd.append("%s" %  _("Update is complete"))
  515.       f = tempfile.NamedTemporaryFile()
  516.       for pkg in self.cache:
  517.           if pkg.markedInstall or pkg.markedUpgrade:
  518.               f.write("%s\tinstall\n" % pkg.name)
  519.       cmd.append("--set-selections-file")
  520.       cmd.append("%s" % f.name)
  521.       f.flush()
  522.       subprocess.call(cmd)
  523.       f.close()
  524.     elif action == UPDATE:
  525.       cmd.append("--update-at-startup")
  526.       subprocess.call(cmd)
  527.     else:
  528.       print "run_synaptic() called with unknown action"
  529.       sys.exit(1)
  530.     lock.release()
  531.  
  532.   def on_button_reload_clicked(self, widget):
  533.     #print "on_button_reload_clicked"
  534.     self.check_metarelease()
  535.     self.invoke_manager(UPDATE)
  536.  
  537.   #def on_button_help_clicked(self, widget):
  538.   #  self.help_viewer.run()
  539.  
  540.   def on_button_settings_clicked(self, widget):
  541.       #print "on_button_settings_clicked"
  542.       try:
  543.           apt_pkg.PkgSystemUnLock()
  544.       except SystemError:
  545.           pass
  546.       cmd = ["/usr/bin/gksu", 
  547.              "--desktop", "/usr/share/applications/software-properties.desktop", 
  548.              "--", "/usr/bin/software-properties-gtk","--open-tab","2",
  549.              "--toplevel", "%s" % self.window_main.window.xid ]
  550.       self.window_main.set_sensitive(False)
  551.       p = subprocess.Popen(cmd)
  552.       while p.poll() is None:
  553.           while gtk.events_pending():
  554.               gtk.main_iteration()
  555.           time.sleep(0.05)
  556.       self.fillstore()
  557.  
  558.   def on_button_install_clicked(self, widget):
  559.     #print "on_button_install_clicked"
  560.     err_sum = _("Not enough free disk space")
  561.     err_long= _("The upgrade needs a total of %s free space on disk '%s'. "
  562.                 "Please free at least an additional %s of disk "
  563.                 "space on '%s'. "
  564.                 "Empty your trash and remove temporary "
  565.                 "packages of former installations using "
  566.                 "'sudo apt-get clean'.")
  567.     # check free space and error if its not enough
  568.     try:
  569.         self.cache.checkFreeSpace()
  570.     except NotEnoughFreeSpaceError, e:
  571.         for req in e.free_space_required_list:
  572.             self.error(err_sum, err_long % (req.size_total,
  573.                                             req.dir,
  574.                                             req.size_needed,
  575.                                             req.dir))
  576.         return
  577.     self.invoke_manager(INSTALL)
  578.     
  579.   def invoke_manager(self, action):
  580.     # check first if no other package manager is runing
  581.  
  582.     # don't display apt-listchanges, we already showed the changelog
  583.     os.environ["APT_LISTCHANGES_FRONTEND"]="none"
  584.  
  585.     # Do not suspend during the update process
  586.     (dev, cookie) = inhibit_sleep()
  587.  
  588.     # set window to insensitive
  589.     self.window_main.set_sensitive(False)
  590.     self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
  591.     lock = thread.allocate_lock()
  592.     lock.acquire()
  593.     t = thread.start_new_thread(self.run_synaptic,
  594.                                 (self.window_main.window.xid,action,lock))
  595.     while lock.locked():
  596.       while gtk.events_pending():
  597.         gtk.main_iteration()
  598.       time.sleep(0.05)
  599.     while gtk.events_pending():
  600.       gtk.main_iteration()
  601.     s = _("Reading package information")
  602.     self.label_cache_progress_title.set_label("<b><big>%s</big></b>" % s)
  603.     self.fillstore()
  604.  
  605.     # Allow suspend after synaptic is finished
  606.     if cookie != False:
  607.         allow_sleep(dev, cookie)
  608.     self.window_main.set_sensitive(True)
  609.     self.window_main.window.set_cursor(None)
  610.  
  611.   def row_activated(self, treeview, path, column):
  612.       iter = self.store.get_iter(path)
  613.       pkg = self.store.get_value(iter, LIST_PKG)
  614.       origin = self.store.get_value(iter, LIST_ORIGIN)
  615.       if pkg is not None:
  616.           return
  617.       self.setBusy(True)
  618.       actiongroup = apt_pkg.GetPkgActionGroup(self.cache._depcache)
  619.       for pkg in self.list.pkgs[origin]:
  620.           if pkg.markedInstall or pkg.markedUpgrade:
  621.               #print "marking keep: ", pkg.name
  622.               pkg.markKeep()
  623.           elif not (pkg.name in self.list.held_back):
  624.               #print "marking install: ", pkg.name
  625.               pkg.markInstall(autoFix=False,autoInst=False)
  626.       # check if we left breakage
  627.       if self.cache._depcache.BrokenCount:
  628.           Fix = apt_pkg.GetPkgProblemResolver(self.cache._depcache)
  629.           Fix.ResolveByKeep()
  630.       self.refresh_updates_count()
  631.       self.treeview_update.queue_draw()
  632.       del actiongroup
  633.       self.setBusy(False)
  634.  
  635.  
  636.   def toggled(self, renderer, path):
  637.     """ a toggle button in the listview was toggled """
  638.     iter = self.store.get_iter(path)
  639.     pkg = self.store.get_value(iter, LIST_PKG)
  640.     # make sure that we don't allow to toggle deactivated updates
  641.     # this is needed for the call by the row activation callback
  642.     if pkg is None or pkg.name in self.list.held_back:
  643.         return False
  644.     self.setBusy(True)
  645.     # update the cache
  646.     if pkg.markedInstall or pkg.markedUpgrade:
  647.         pkg.markKeep()
  648.         if self.cache._depcache.BrokenCount:
  649.             Fix = apt_pkg.GetPkgProblemResolver(self.cache._depcache)
  650.             Fix.ResolveByKeep()
  651.     else:
  652.         pkg.markInstall()
  653.     self.treeview_update.queue_draw()
  654.     self.refresh_updates_count()
  655.     self.setBusy(False)
  656.  
  657.   def on_treeview_update_row_activated(self, treeview, path, column, *args):
  658.       """
  659.       If an update row was activated (by pressing space), toggle the 
  660.       install check box
  661.       """
  662.       self.toggled(None, path)
  663.  
  664.   def exit(self):
  665.     """ exit the application, save the state """
  666.     self.save_state()
  667.     #gtk.main_quit()
  668.     sys.exit(0)
  669.  
  670.   def save_state(self):
  671.     """ save the state  (window-size for now) """
  672.     (x,y) = self.window_main.get_size()
  673.     try:
  674.         self.gconfclient.set_pair("/apps/update-manager/window_size",
  675.                                   gconf.VALUE_INT, gconf.VALUE_INT, x, y)
  676.     except gobject.GError, e:
  677.         print "Could not save the configuration to gconf: %s" % e
  678.         pass
  679.  
  680.   def restore_state(self):
  681.     """ restore the state (window-size for now) """
  682.     expanded = self.gconfclient.get_bool("/apps/update-manager/show_details")
  683.     self.expander_details.set_expanded(expanded)
  684.     self.vbox_updates.set_child_packing(self.expander_details,
  685.                                         expanded,
  686.                                         True,
  687.                                         0,
  688.                                         True)
  689.     (x,y) = self.gconfclient.get_pair("/apps/update-manager/window_size",
  690.                                       gconf.VALUE_INT, gconf.VALUE_INT)
  691.     if x > 0 and y > 0:
  692.       self.window_main.resize(x,y)
  693.  
  694.   def fillstore(self):
  695.     # use the watch cursor
  696.     self.setBusy(True)
  697.     # clean most objects
  698.     self.dl_size = 0
  699.     try:
  700.         self.initCache()
  701.     except SystemError, e:
  702.         msg = ("<big><b>%s</b></big>\n\n%s\n'%s'" %
  703.                (_("Could not initialize the package information"),
  704.                 _("An unresolvable problem occurred while "
  705.                   "initializing the package information.\n\n"
  706.                   "Please report this bug against the 'update-manager' "
  707.                   "package and include the following error message:\n"),
  708.                 e)
  709.                )
  710.         dialog = gtk.MessageDialog(self.window_main,
  711.                                    0, gtk.MESSAGE_ERROR,
  712.                                    gtk.BUTTONS_CLOSE,"")
  713.         dialog.set_markup(msg)
  714.         dialog.vbox.set_spacing(6)
  715.         dialog.run()
  716.         dialog.destroy()
  717.         sys.exit(1)
  718.     self.store.clear()
  719.     self.list = UpdateList(self)
  720.     # fill them again
  721.     try:
  722.         self.list.update(self.cache)
  723.     except SystemError, e:
  724.         msg = ("<big><b>%s</b></big>\n\n%s\n'%s'" %
  725.                (_("Could not calculate the upgrade"),
  726.                 _("An unresolvable problem occurred while "
  727.                   "calculating the upgrade.\n\n"
  728.                   "Please report this bug against the 'update-manager' "
  729.                   "package and include the following error message:"),
  730.                 e)
  731.                )
  732.         dialog = gtk.MessageDialog(self.window_main,
  733.                                    0, gtk.MESSAGE_ERROR,
  734.                                    gtk.BUTTONS_CLOSE,"")
  735.         dialog.set_markup(msg)
  736.         dialog.vbox.set_spacing(6)
  737.         dialog.run()
  738.         dialog.destroy()
  739.     if self.list.num_updates > 0:
  740.       origin_list = self.list.pkgs.keys()
  741.       origin_list.sort(lambda x,y: cmp(x.importance,y.importance))
  742.       origin_list.reverse()
  743.       for origin in origin_list:
  744.         self.store.append(['<b><big>%s</big></b>' % origin.description,
  745.                            origin.description, None, origin])
  746.         for pkg in self.list.pkgs[origin]:
  747.           name = xml.sax.saxutils.escape(pkg.name)
  748.           summary = xml.sax.saxutils.escape(pkg.summary)
  749.           contents = "<b>%s</b>\n<small>%s</small>" % (name, summary)
  750.           #TRANSLATORS: the b stands for Bytes
  751.           size = _("(Size: %s)") % humanize_size(pkg.packageSize)
  752.           if pkg.installedVersion != None:
  753.               version = _("From version %(old_version)s to %(new_version)s") %\
  754.                   {"old_version" : pkg.installedVersion,
  755.                    "new_version" : pkg.candidateVersion}
  756.           else:
  757.               version = _("Version %s") % pkg.candidateVersion
  758.           if self.show_versions:
  759.               contents = "%s\n<small>%s %s</small>" % (contents, version, size)
  760.           else:
  761.               contents = "%s <small>%s</small>" % (contents, size)
  762.           self.store.append([contents, pkg.name, pkg, None])
  763.     self.update_count()
  764.     self.setBusy(False)
  765.     self.check_all_updates_installable()
  766.     return False
  767.  
  768.   def dist_no_longer_supported(self, meta_release):
  769.     msg = "<big><b>%s</b></big>\n\n%s" % \
  770.           (_("Your distribution is not supported anymore"),
  771.        _("You will not get any further security fixes or critical "
  772.              "updates. "
  773.              "Upgrade to a later version of Ubuntu Linux. See "
  774.              "http://www.ubuntu.com for more information on "
  775.              "upgrading."))
  776.     dialog = gtk.MessageDialog(self.window_main, 0, gtk.MESSAGE_WARNING,
  777.                                gtk.BUTTONS_CLOSE,"")
  778.     dialog.set_title("")
  779.     dialog.set_markup(msg)
  780.     dialog.run()
  781.     dialog.destroy()
  782.  
  783.   def error(self, summary, details):
  784.       " helper function to display a error message "
  785.       msg = ("<big><b>%s</b></big>\n\n%s\n" % (summary, details) )
  786.       dialog = gtk.MessageDialog(self.window_main,
  787.                                  0, gtk.MESSAGE_ERROR,
  788.                                  gtk.BUTTONS_CLOSE,"")
  789.       dialog.set_markup(msg)
  790.       dialog.vbox.set_spacing(6)
  791.       dialog.run()
  792.       dialog.destroy()
  793.  
  794.   def on_button_dist_upgrade_clicked(self, button):
  795.       #print "on_button_dist_upgrade_clicked"
  796.       fetcher = DistUpgradeFetcherGtk(new_dist=self.new_dist, parent=self, progress=GtkProgress.GtkFetchProgress(self))
  797.       if self.options.sandbox:
  798.           fetcher.run_options.append("--sandbox")
  799.       fetcher.run()
  800.       
  801.   def new_dist_available(self, meta_release, upgradable_to):
  802.     self.frame_new_release.show()
  803.     self.label_new_release.set_markup(_("<b>New distribution release '%s' is available</b>") % upgradable_to.version)
  804.     self.new_dist = upgradable_to
  805.     
  806.  
  807.   # fixme: we should probably abstract away all the stuff from libapt
  808.   def initCache(self): 
  809.     # get the lock
  810.     try:
  811.         apt_pkg.PkgSystemLock()
  812.     except SystemError, e:
  813.         pass
  814.         #d = gtk.MessageDialog(parent=self.window_main,
  815.         #                      flags=gtk.DIALOG_MODAL,
  816.         #                      type=gtk.MESSAGE_ERROR,
  817.         #                      buttons=gtk.BUTTONS_CLOSE)
  818.         #d.set_markup("<big><b>%s</b></big>\n\n%s" % (
  819.         #    _("Only one software management tool is allowed to "
  820.         #      "run at the same time"),
  821.         #    _("Please close the other application e.g. 'aptitude' "
  822.         #      "or 'Synaptic' first.")))
  823.         #print "error from apt: '%s'" % e
  824.         #d.set_title("")
  825.         #res = d.run()
  826.         #d.destroy()
  827.         #sys.exit()
  828.  
  829.     try:
  830.         if hasattr(self, "cache"):
  831.             self.cache.open(self.progress)
  832.             self.cache._initDepCache()
  833.         else:
  834.             self.cache = MyCache(self.progress)
  835.     except AssertionError:
  836.         # if the cache could not be opened for some reason,
  837.         # let the release upgrader handle it, it deals
  838.         # a lot better with this
  839.         self.ask_run_partial_upgrade()
  840.         # we assert a clean cache
  841.         msg=("<big><b>%s</b></big>\n\n%s"% \
  842.              (_("Software index is broken"),
  843.               _("It is impossible to install or remove any software. "
  844.                 "Please use the package manager \"Synaptic\" or run "
  845.         "\"sudo apt-get install -f\" in a terminal to fix "
  846.         "this issue at first.")))
  847.         dialog = gtk.MessageDialog(self.window_main,
  848.                                    0, gtk.MESSAGE_ERROR,
  849.                                    gtk.BUTTONS_CLOSE,"")
  850.         dialog.set_markup(msg)
  851.         dialog.vbox.set_spacing(6)
  852.         dialog.run()
  853.         dialog.destroy()
  854.         sys.exit(1)
  855.     else:
  856.         self.progress.hide()
  857.  
  858.   def check_auto_update(self):
  859.       # Check if automatic update is enabled. If not show a dialog to inform
  860.       # the user about the need of manual "reloads"
  861.       remind = self.gconfclient.get_bool("/apps/update-manager/remind_reload")
  862.       if remind == False:
  863.           return
  864.  
  865.       update_days = apt_pkg.Config.FindI("APT::Periodic::Update-Package-Lists")
  866.       if update_days < 1:
  867.           self.dialog_manual_update.set_transient_for(self.window_main)
  868.           res = self.dialog_manual_update.run()
  869.           self.dialog_manual_update.hide()
  870.           if res == gtk.RESPONSE_YES:
  871.               self.on_button_reload_clicked(None)
  872.  
  873.   def check_all_updates_installable(self):
  874.     """ Check if all available updates can be installed and suggest
  875.         to run a distribution upgrade if not """
  876.     if self.list.distUpgradeWouldDelete > 0:
  877.         self.ask_run_partial_upgrade()
  878.  
  879.   def ask_run_partial_upgrade(self):
  880.       self.dialog_dist_upgrade.set_transient_for(self.window_main)
  881.       res = self.dialog_dist_upgrade.run()
  882.       self.dialog_dist_upgrade.hide()
  883.       if res == gtk.RESPONSE_YES:
  884.           os.execl("/usr/bin/gksu",
  885.                    "/usr/bin/gksu", "--desktop",
  886.                    "/usr/share/applications/update-manager.desktop",
  887.                    "--", "/usr/bin/update-manager", "--dist-upgrade")
  888.       return False
  889.  
  890.   def check_metarelease(self):
  891.       " check for new meta-release information "
  892.       gconfclient = gconf.client_get_default()
  893.       self.meta = MetaRelease(self.options.devel_release,
  894.                               self.options.use_proposed)
  895.       self.meta.connect("dist_no_longer_supported",self.dist_no_longer_supported)
  896.       # check if we are interessted in dist-upgrade information
  897.       # (we are not by default on dapper)
  898.       if self.options.check_dist_upgrades or \
  899.              gconfclient.get_bool("/apps/update-manager/check_dist_upgrades"):
  900.           self.meta.connect("new_dist_available",self.new_dist_available)
  901.       
  902.  
  903.   def main(self, options):
  904.     self.options = options
  905.  
  906.     # check for new distributin information
  907.     self.check_metarelease()
  908.  
  909.     while gtk.events_pending():
  910.       gtk.main_iteration()
  911.  
  912.     self.fillstore()
  913.     self.check_auto_update()
  914.     gtk.main()
  915.